﻿using System;
using System.Collections.Generic;
using System.Linq;
using LoungeRepo.Contracts.Normalizer;
using LoungeRepo.Normalizer.DataStructures;

namespace LoungeRepo.Normalizer
{
    partial class ObjectGraphDenormalizer
    {
        private void DenormalizeNonTransientFields(INormalizedObject source, object destination)
        {
            foreach (var field in destination.AllFieldsOfObjectType())
                if (!field.IsNotSerialized)
                    // ReSharper disable AccessToModifiedClosure
                    field.SetValue(destination, DenormalizeFieldValue(source.Fields.Single(nf => nf.Name == field.Name).Value));
                    // ReSharper restore AccessToModifiedClosure
        }


        private object DenormalizeFieldValue(object fieldValue)
        {
            object denormalizedValue;

            if (TryDenormalizeComplexTypeValue(fieldValue, out denormalizedValue))
                return denormalizedValue;

            return fieldValue;
        }


        private bool TryDenormalizeComplexTypeValue(object value, out object denormalizedValue)
        {
            var objectRef = value as INormalizedObjectReference;

            if (objectRef != null)
                if (this.denormalizedObjectCache.ContainsKey(objectRef.RefId))
                {
                    denormalizedValue = this.denormalizedObjectCache[objectRef.RefId];
                    return true;
                }
                else if (objectRef is NormalizedEntityReference)
                {
                    denormalizedValue = this.resolveEntityReference(objectRef as NormalizedEntityReference);
                    if (denormalizedValue is INormalizedEntity)
                        denormalizedValue = Denormalize(denormalizedValue as INormalizedEntity);
                    return true;
                }

            if (value is INormalizedObject)
            {
                denormalizedValue = Denormalize(value as INormalizedObject);
                return true;
            }

            //TODO: more checks for other types to denormalize needed?

            denormalizedValue = value;
            return false;
        }


        private void TransferElementsIntoArray(IList<INormalizedField> normalizedFields, Array denormalizedArray)
        {
            int fieldIndex = denormalizedArray.Rank + 1;
            TransferArrayElementsInRank(normalizedFields, denormalizedArray, 0, ref fieldIndex, new int[denormalizedArray.Rank]);
        }


        private void TransferArrayElementsInRank(IList<INormalizedField> normalizedFields, Array denormalizedArray, int currentRank, ref int fieldIndex, int[] indexInDimensions)
        {
            for(int i=0; i<denormalizedArray.GetLength(currentRank); i++)
            {
                indexInDimensions[currentRank] = i;
                if (currentRank < denormalizedArray.Rank - 1)
                    TransferArrayElementsInRank(normalizedFields, denormalizedArray, currentRank + 1, ref fieldIndex, indexInDimensions);
                else
                {
                    denormalizedArray.SetValue(DenormalizeFieldValue(normalizedFields[fieldIndex].Value), indexInDimensions);
                    fieldIndex++;
                }
            }
        }
    }
}
